어댑터 패턴
어댑터 패턴이란?
어댑터 패턴은 클래스의 인터페이스를 사용자가 기대하는 다른 인터페이스로 변화하는 패턴으로, 호환성이 없는 인터페이스 때문에 함께 동작할 수 없는 클래스들이 함께 동작하도록 해주는 패턴이다. 즉, 어댑터 패턴은 B를 A처럼 포장하여 A로 사용할 수 있게 하는 패턴이다.
위의 그림은 흔히 외국으로 여행을 갈 때 꼭 챙기는 물품 중 하나인 어댑터이다. 이를 생각하면 왜 어댑터 패턴이라고 불렸는지 이해하기 쉽다.
위의 물품은 특정 규격의 플러그를 다른 플러그와 호환 가능
하게 한다. 이처럼 어댑터 패턴은 객체 지향 프로그래밍에서 하나의 코드를 다른 코드와 호환시키려는 목적으로 사용된다.
오브젝트 어댑터
어댑터 패턴에는 두가지 종류가 있는데 그중 하나인 오브젝트 어댑터에 대해서 LePUS3형식으로 표현된 그림과 함께 알아보자.
각각의 단어에 대한 설명은 다음과 같다.
- Client : Target 인터페이스를 요구하는 요소를 지닌 클래스
- Target : 어떤 인터페이스
- Adaptee : Client의 Target 인터페이스를 요구하는 요소에 집어넣고 싶은 클래스. Target 인터페이스에 호환되지 않음.
- Adapter : Adaptee 클래스를 Target 인터페이스에 맞춰주는 클래스
여기서 Adapter는 Target 인터페이스의 request() 메소드를 구현 하기 위해 Adaptee 클래스의 specific_request() 메소드를 사용 할 것이다.
그러면 Client는 Adapter 클래스를 통해 Adaptee를 Target 인터페이스의 클래스처럼 사용할 수 있다.
이를 파이썬 코드로 다음과 같이 표현할 수 있다.
# Python code sample
class Target(object):
def specific_request(self):
return 'Hello Adapter Pattern!'
class Adapter(object):
def __init__(self, adaptee):
self.adaptee = adaptee
def request(self):
return self.adaptee.specific_request()
client = Adapter(Target())
print(client.request())
클래스 어댑터
또다른 종류인 클래스 어댑터이다. 이또한 LePUS3형식으로 표현된 그림과 함께 확인해보자.
각각의 다이어그램은 오브젝트 어댑터와 같다.
차이점은, 클래스 어댑터는 다중 상속을 사용하고 Adapter가 Target 클래스와 Adaptee 클래스를 둘 다 상속하여 Target 클래스가 필요한 곳에서도 사용 될 수 있고 Adaptee 클래스가 필요한 곳에서도 사용 될 수 있게 한다.
오브젝트 어댑터 vs 클래스 어댑터
오브젝트 어댑터 | 클래스 어댑터 | |
---|---|---|
장점 | 상속이 아닌 컴포지션(Composition)을 사용하기 때문에 더 유연하다 | Adapter가 Adaptee의 서브클래스이기 때문에 Adaptee의 행동을 오버라이드 할 수 있다. Adaptee 객체를 만들지 않아도 된다. |
단점 | Adaptee 객체를 만들어야 사용가능하다. | 상속을 이용하기 때문에 한 Adapter 클래스가 특정 Adatee 클래스에만 적용가능하다. 다중상속이 지원되는 언어에서만 사용가능하다. |
상속 vs 컴포지션
상속 : 하위 클래스가 상위 클래스의 특성을 재정의한 것
컴포지션 : 기존 클래스가 새로운 클래스의 구성요소가 되는 것
어댑터 패턴 장단점
위에서 어댑터 패턴의 종류와 각각의 비교를 해보았다. 이제 어댑터 패턴의 장단점에 대해 알아보자
- 장점:
- 기존 클라이언트 단의 코드 수정 최소화
- 클라이언트는 연동부분을 몰라도, 새로운 코드의 기능을 일관되게 사용 가능
- 단점:
- 어댑터 클래스에서 통일 시켜주는 부분을 하나씩 구현해야함
어댑터 패턴 코드 예시
"""
어댑터 패턴 코드 예시
안드로이드와 아이폰 충전하는 방법이 정의되었을때,
아이폰을 안드로이드 어댑터로 충전하는 방법
"""
from abc import ABCMeta, abstractmethod
NOT_IMPLEMENTED = "구현 예제"
RECHARGE = ["충전이 시작되었습니다.", "충전이 끝났습니다."]
POWER_ADAPTERS = {"Android": "MicroUSB", "iPhone": "Lightning"}
CONNECTED = "{} connected."
CONNECT_FIRST = "Connect {} first."
class RechargeTemplate:
__metaclass__ = ABCMeta
@abstractmethod
def recharge(self):
raise NotImplementedError(NOT_IMPLEMENTED)
class FormatIPhone(RechargeTemplate):
@abstractmethod
def use_lightning(self):
raise NotImplementedError(NOT_IMPLEMENTED)
class FormatAndroid(RechargeTemplate):
@abstractmethod
def use_micro_usb(self):
raise NotImplementedError(NOT_IMPLEMENTED)
class IPhone(FormatIPhone):
__name__ = "iPhone"
def __init__(self):
self.connector = False
def use_lightning(self):
self.connector = True
print(CONNECTED.format(POWER_ADAPTERS[self.__name__]))
def recharge(self):
if self.connector:
for state in RECHARGE:
print(state)
else:
print(CONNECT_FIRST.format(POWER_ADAPTERS[self.__name__]))
class Android(FormatAndroid):
__name__ = "Android"
def __init__(self):
self.connector = False
def use_micro_usb(self):
self.connector = True
print(CONNECTED.format(POWER_ADAPTERS[self.__name__]))
def recharge(self):
if self.connector:
for state in RECHARGE:
print(state)
else:
print(CONNECT_FIRST.format(POWER_ADAPTERS[self.__name__]))
class IPhoneAdapter(FormatAndroid):
def __init__(self, mobile):
self.mobile = mobile
def recharge(self):
self.mobile.recharge()
def use_micro_usb(self):
print(CONNECTED.format(POWER_ADAPTERS["Android"]))
self.mobile.use_lightning()
class AndroidRecharger:
def __init__(self):
self.phone = Android()
self.phone.use_micro_usb()
self.phone.recharge()
class IPhoneMicroUSBRecharger:
def __init__(self):
self.phone = IPhone()
self.phone_adapter = IPhoneAdapter(self.phone)
self.phone_adapter.use_micro_usb()
self.phone_adapter.recharge()
class IPhoneRecharger:
def __init__(self):
self.phone = IPhone()
self.phone.use_lightning()
self.phone.recharge()
print("MicroUSB 충전기로 안드로이드 충전하기")
AndroidRecharger()
print()
print("adapter pattern을 사용해서 MicroUSB 충전기로 아이폰 충전하기")
IPhoneMicroUSBRecharger()
print()
print("아이폰 충전기로 아이폰 충전하기")
IPhoneRecharger()
출력 결과
MicroUSB 충전기로 안드로이드 충전하기
MicroUSB connected.
충전이 시작되었습니다.
충전이 끝났습니다.
adapter pattern을 사용해서 MicroUSB 충전기로 아이폰 충전하기
MicroUSB connected.
Lightning connected.
충전이 시작되었습니다.
충전이 끝났습니다.
아이폰 충전기로 아이폰 충전하기
Lightning connected.
충전이 시작되었습니다.
충전이 끝났습니다.
나올 수 있는 면접 질문
- 어댑터 패턴이란 무엇인가요?
- 어댑터 패턴의 구현 방식의 종류는 무엇이 있나요?
- 어댑터 패턴의 장단점은 무엇인가요?
참고
기여자
Kyun Heo
📦